package spring.aop;

import spring.annotation.After;
import spring.annotation.AfterReturning;
import spring.annotation.AfterThrowing;
import spring.annotation.Aspect;
import spring.annotation.Before;
import spring.beans.BeanFactoryAware;
import spring.beans.BeanPostProcessor;
import spring.beans.factory.BeanFactory;
import spring.beans.factory.DefaultListableBeanFactory;
import spring.utils.CollectionUtils;

import java.lang.reflect.Method;
import java.util.*;

/**
 * 实际上继承AbstractAutoProxyCreator
 *
 */
public class AnnotationAwareAspectJAutoProxyCreator implements BeanPostProcessor, BeanFactoryAware {

    // 当前的bean工厂
    private DefaultListableBeanFactory beanFactory;


    // 所有Advisor
    List<Advisor> candidateAdvisors = new ArrayList<>();


    // aware接口的实现，获得Bean工厂实例
    @Override
    public void setBeanFactory(BeanFactory bf) {
        this.beanFactory = (DefaultListableBeanFactory) bf;
    }

    /**
     * Bean初始化后进行增强功能。
     * 需要在不改变代码的情况实现该功能，则通过代理模式进行增强。
     * @param bean 需要增强的bean
     * @param beanName bean名称
     * @return 最终被增强的的bean，此时的bean已经经过了代理模式的增强。
     * @throws Throwable
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {

        return wrapIfNecessary(bean, beanName);

    }

    /**
     * 返回最终代理对象
     * @param bean
     * @param beanName
     * @return
     */
    private Object wrapIfNecessary(Object bean, String beanName) throws Exception {

        // 在此判断bean是否需要进行切面增强，及获得增强的通知实现
        List<Advisor> matchAdvisors = findEligibleAdvisors(bean.getClass(), beanName);
        // 如需要就进行增强，再返回增强的对象。
        if (!CollectionUtils.isEmpty(matchAdvisors)) {
            System.out.println("我进来了");
            // 返回代理对象
            bean = createProxy(bean, beanName, matchAdvisors, this.beanFactory);

        }
        return bean;
    }

    /**
     * 筛选当前类所含有的所有Advisor
     * @param beanClass
     * @param beanName
     * @return
     */
    private List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {

        List<Advisor> candidateAdvisor = findCandidateAdvisors();

        if (CollectionUtils.isEmpty(candidateAdvisor)) {
            return null;
        }

        // 得到类中的所有的方法
        List<Method> allMethods = Arrays.asList(beanClass.getDeclaredMethods());

        // 存放匹配的Advisor的list
        List<Advisor> matchAdvisors = new ArrayList<>();
        // 遍历Advisor来找匹配的
        for (Advisor ad : candidateAdvisor) {
            if (ad instanceof PointcutAdvisor) {
                // 筛选类和方法
                if (isPointcutMatchBean((PointcutAdvisor) ad, beanClass, allMethods)) {
                    matchAdvisors.add(ad);
                }
            }
        }

        return matchAdvisors;
    }

    /**
     * 获得全局所有的Advisor
     * @return
     */
    private List<Advisor> findCandidateAdvisors() {
        if (this.candidateAdvisors.size() != 0){
            return this.candidateAdvisors;
        }

        List<Advisor> advisors = new ArrayList<>();

        // 找到切面类
        for (String beanName : beanFactory.getBeanDefinitionNames()){
            Class<?> beanClass = beanFactory.getType(beanName);
            if (beanClass.isAnnotationPresent(Aspect.class)){
                // 遍历所有方法
                for (Method m : beanClass.getDeclaredMethods()){
                    // 通知名称
                    String adviceBeanName = beanName+m.getName();
                    if (m.isAnnotationPresent(spring.annotation.Before.class)){
                        String expression = m.getAnnotation(Before.class).value();
                        advisors.add(new AspectJPointcutAdvisor(adviceBeanName,expression));

                    } else if (m.isAnnotationPresent(AfterThrowing.class)) {
                        String expression = m.getAnnotation(AfterThrowing.class).value();
                        advisors.add(new AspectJPointcutAdvisor(adviceBeanName,expression));


                    } else if (m.isAnnotationPresent(AfterReturning.class)) {
                        String expression = m.getAnnotation(AfterReturning.class).value();
                        advisors.add(new AspectJPointcutAdvisor(adviceBeanName,expression));


                    }  else if (m.isAnnotationPresent(After.class)) {
                        String expression = m.getAnnotation(After.class).value();
                        advisors.add(new AspectJPointcutAdvisor(adviceBeanName,expression));

                    }

                }
            }
        }
        this.candidateAdvisors.addAll(advisors);
        return this.candidateAdvisors;
    }


    /**
     * 判断指定类中的方法是否有符合切点规则的。
     * @param pa	方面信息，带有切点对象
     * @param beanClass	指定的类
     * @param methods	指定类中的所有方法
     * @return
     */
    private boolean isPointcutMatchBean(PointcutAdvisor pa, Class<?> beanClass, List<Method> methods) {
        Pointcut p = pa.getPointcut();

        // 首先判断类是否匹配
        if (!p.matchsClass(beanClass)) {
            return false;
        }

        // 再判断是否有方法匹配
        for (Method method : methods) {
            if (p.matchsMethod(method, beanClass)) {
                return true;
            }
        }
        return false;
    }

    private Object createProxy(Object bean, String beanName, List<Advisor> matchAdvisors,BeanFactory beanFactory) throws Exception {
                // 获得代理工厂实现
//        return new AopProxyFactory()
//                // 根据参数信息，选择创建代理工厂具体实现
//                .createAopProxy(bean, beanName, matchAdvisors, beanFactory)
//                // 从选择的代理工厂中，获得代理对象
//                .getProxy();
        return bean;
    }
}
